﻿using System;
using complex.Behaviors;
using complex.Controllers;
using complex.Services;
using FluentValidation;
using FubuMVC.Core;
using FubuMVC.Core.Registration.Nodes;
using FubuMVC.Spark;
using FubuMVC.Validation;
using FubuValidation;
using StructureMap;

namespace complex.Registries
{
    public class ComplexRegistry
        : FubuRegistry
    {
        public ComplexRegistry()
        {
            IncludeDiagnostics(true);

            Routes
                .IgnoreControllerNamespaceEntirely()
                .ConstrainToHttpMethod(call => call.InputEndsWith("query"), "GET")
                .ConstrainToHttpMethod(call => call.InputEndsWith("command"), "POST")
                .HomeIs<HomeInputModel>();

            Policies
                .ConditionallyWrapBehaviorChainsWith<TransactionBehavior>(call => call.RequiresTransaction());

            Actions
                .IncludeClassesSuffixedWithController();

            Views
                .TryToAttachWithDefaultConventions()
                .RegisterActionLessViews(t => t.ViewModelType == typeof (Notification));

            Output
                .To<SparkHtmlTagOutput>().WhenTheOutputModelIs<SparkHtmlTag>();

            this.UseSpark();

            Services(s => s.SetServiceIfNone<FubuValidation.IValidator, FluentValidatorForFubuAdaptor>());

            this.Validation(r =>
                                {
                                    r.Actions.Include(call => call.HasAssociatedValidator());
                                    r.Failures.If(f => f.Notification.IsValid() == false).TransferBy<ReturnToPreviousAction>();
                                });
        }
    }

    internal static class ActionCallExtensions
    {
        private static readonly Type OpenValidator = typeof (IValidator<>);

        internal static bool HasAssociatedValidator(this ActionCall call)
        {
            if (call.HasInput)
            {
                var input = call.InputType();
                var closedValidator = OpenValidator.MakeGenericType(input);
                return ObjectFactory.Model.HasImplementationsFor(closedValidator);
            }
            return false;
        }

        internal static bool InputEndsWith(this ActionCall call, string suffix)
        {
            return call.HasInput && call.InputType().Name.ToLower().EndsWith(suffix);
        }

        internal static bool RequiresTransaction(this ActionCall call)
        {
            return call.HasAttribute<SkipTransactionAttribute>() == false;
        }
    }
}